#include "idt.h"
#include "exceptions.h"
#include "println.h"
#include "io.h"

extern void irq_1(void);
extern void isr_irq1(unsigned long int erro);

idt_entry_t idt_entries[IDT_TABLE_SIZE];

void set_offset(idt_entry_t* idt_entries, unsigned int offset);

void init_IDT(void)
{
	unsigned int i;
	
	cli();
	
	for(i = 0; i < 20; i++)
	{
		set_offset(&idt_entries[i], (unsigned int)nullisr);
		idt_entries[i].seg_sel = IDT_SEG;
		idt_entries[i].unused = 0;
		idt_entries[i].flags = IDT_FLAGS;
	}
	
	set_offset(&idt_entries[0], (unsigned int)_ex_div);
	set_offset(&idt_entries[1], (unsigned int)_ex_debug);
	set_offset(&idt_entries[2], (unsigned int)_ex_nmi);
	set_offset(&idt_entries[3], (unsigned int)_ex_break);
	set_offset(&idt_entries[4], (unsigned int)_ex_overflow);
	set_offset(&idt_entries[5], (unsigned int)_ex_bounds);
	set_offset(&idt_entries[6], (unsigned int)_ex_inv_op);		
	set_offset(&idt_entries[7], (unsigned int)_ex_device);
	set_offset(&idt_entries[8], (unsigned int)_ex_dfault);
	set_offset(&idt_entries[9], (unsigned int)_ex_coproc);
	set_offset(&idt_entries[10], (unsigned int)_ex_inv_tss);
	set_offset(&idt_entries[11], (unsigned int)_ex_segnp);
	set_offset(&idt_entries[12], (unsigned int)_ex_stack);
	set_offset(&idt_entries[13], (unsigned int)_ex_gp);
	
	set_offset(&idt_entries[14], (unsigned int)_ex_pfault);
	
	set_offset(&idt_entries[16], (unsigned int)_ex_floatp);
	set_offset(&idt_entries[17], (unsigned int)_ex_align);
	set_offset(&idt_entries[18], (unsigned int)_ex_machine);
	set_offset(&idt_entries[19], (unsigned int)_ex_simd);

	/*Intel reserved.*/
	set_offset(&idt_entries[15], (unsigned int)0x00000000);
	idt_entries[15].flags = IDT_FLAGS_R;
	for(i = 20; i < 32; i++)
	{
		set_offset(&idt_entries[i], (unsigned int)nullisr);
		idt_entries[i].seg_sel = IDT_SEG;
		idt_entries[i].unused = 0;
		idt_entries[i].flags = IDT_FLAGS_R;
	}
	
	/*IRQs*/
	for(i = 32; i < 48; i++)
	{
		set_offset(&idt_entries[i], (unsigned int)nullisr);
		idt_entries[i].seg_sel = IDT_SEG;
		idt_entries[i].unused = 0;
		idt_entries[i].flags = IDT_FLAGS;
	}
	
	set_offset(&idt_entries[33],  (unsigned int)irq_1);
	
	/*User defined IRQs*/
	for(i = 48; i < IDT_TABLE_SIZE; i++)
	{
		set_offset(&idt_entries[i], (unsigned int)nullisr);
		idt_entries[i].seg_sel = IDT_SEG;
		idt_entries[i].unused = 0;
		idt_entries[i].flags = IDT_FLAGS | 0x60;
	}

	IDTR.table_limit = (IDT_TABLE_SIZE * sizeof(idt_entry_t)) - 1;
	IDTR.idt_ptr     = (unsigned int)idt_entries;
	load_idt();
}

inline void load_idt(void)
{
	asm("lidtl %0" : : "m" (IDTR));
}

void set_offset(idt_entry_t* idt_entries, unsigned int offset)
{
	idt_entries->l_offset = offset & 0xFFFF;
	idt_entries->h_offset = (offset >> 16) & 0xFFFF;	
}

